#pragma once

#include <stx/core/inc.hpp>

inline namespace stx
{
template<typename T>
concept IsRaw = std::same_as<T, NoCvr<T>>;

template<typename T>
concept IsRawNonVoid = IsRaw<T> && IsNonVoid<T>;

template<typename T>
concept IsCopyable = requires(NoCvr<T>& a, NoCvr<T> const& b)
{
    a = b;
    NoCvr<T>(b);
    requires std::same_as<decltype(a = b), Ref<NoCvr<T>>>;
};

template<typename T>
concept IsMovable = requires(NoCvr<T>& a, NoCvr<T>&& b)
{
    a = std::move(b);
    NoCvr<T>(std::move(b));
    requires std::same_as<decltype(a = std::move(b)), Ref<NoCvr<T>>>;
};

template<typename T>
concept IsTransferable = std::is_default_constructible_v<NoCvr<T>> &&
    std::is_destructible_v<NoCvr<T>> && AnyTrue<IsCopyable<T>, IsMovable<T>>;

template<typename T>
concept IsRawTransferable = IsTransferable<T> && IsRaw<T>;

template<typename T, typename U>
concept IsBaseOf = std::is_base_of_v<NoCvr<T>, NoCvr<U>>;

template<typename T>
concept IsPointer = std::is_pointer_v<NoCvr<T>>;

template<typename T>
concept IsConst = std::is_const_v<std::remove_reference_t<T>>;

template<typename T>
concept IsRef = std::is_reference_v<T>;

template<typename T>
concept IsLvRef = std::is_lvalue_reference_v<T>;

template<typename T>
concept IsRvRef = std::is_rvalue_reference_v<T>;

template<typename T>
concept IsClvRef = std::is_lvalue_reference_v<T> && IsConst<T>;

template<typename T>
concept IsCrvRef = std::is_rvalue_reference_v<T> && IsConst<T>;

template<typename T>
concept IsIntegral = std::integral<NoCvr<T>>;

template<typename T>
concept IsRawIntegral = IsIntegral<T> && IsRaw<T>;

template<typename T>
concept IsFloatingPoint = std::floating_point<NoCvr<T>>;

template<typename T>
concept IsRawFloatingPoint = IsFloatingPoint<T> && IsRaw<T>;

template<typename T>
concept IsArithmetic = std::is_arithmetic_v<NoCvr<T>>;

template<typename T>
concept IsRawArithmetic = IsArithmetic<T> && IsRaw<T>;

template<typename T>
concept IsEnum = std::is_enum_v<NoCvr<T>>;

template<typename T>
concept IsRawEnum = IsEnum<T> && IsRaw<T>;

template<typename T>
concept IsIntegralLike = IsIntegral<T> || IsEnum<T>;

template<typename T>
concept IsRawIntegralLike = IsRawIntegral<T> || IsRawEnum<T>;

template<typename T>
concept IsRange = ranges::range<T>;

template<typename T>
concept IsInputRange = ranges::input_range<T>;

template<typename T>
concept IsForwardRange = ranges::forward_range<T>;

template<typename T>
concept IsBidirectionalRange = ranges::bidirectional_range<T>;

template<typename T>
concept IsRandomAccessRange = ranges::random_access_range<T>;

template<typename T>
concept IsContiguousRange = ranges::contiguous_range<T>;

template<typename T>
concept IsSizedRange = ranges::sized_range<T>;

template<typename T>
concept IsViewableRange = ranges::viewable_range<T>;

template<typename T>
concept IsBorrowedRange = ranges::borrowed_range<T>;

template<IsRange T>
using RangeValueType = ranges::range_value_t<NoCvr<T>>;

template<IsRange T>
constexpr auto RangeValueSize = sizeof(RangeValueType<T>);

template<typename T, typename U>
concept IsIdentical = std::same_as<NoCvr<T>, NoCvr<U>>;

template<typename T>
concept IsPod =
    std::is_trivial_v<NoCvr<T>> && std::is_standard_layout_v<NoCvr<T>>;

template<typename T>
concept IsRawPod = IsPod<T> && IsRaw<T>;

template<typename T>
concept IsOctet = IsPod<T> && sizeof(T) == 1;

template<typename T>
concept IsRawOctet = IsRaw<T> && IsOctet<T>;

template<typename T>
concept IsContiguousPodRange = IsContiguousRange<T> && IsPod<RangeValueType<T>>;

template<typename T>
concept IsMemArea = IsContiguousPodRange<T> || IsPod<T>;

template<typename T>
concept IsMemAreaRange = IsRange<T> && requires
{
    requires IsMemArea<RangeValueType<T>>;
};

template<typename T>
concept IsPen = AnyTrue<IsPointer<T>, IsEnum<T>, IsIntegral<T>>;

template<typename T>
concept IsRawPen = IsPen<T> && IsRaw<T>;

template<typename T>
concept IsContiguousAnsiCharRange = IsContiguousPodRange<T> && requires
{
    requires EXPR(IsIdentical<RangeValueType<T>, char>);
};

template<typename T>
concept IsBoolLike = requires(T&& b)
{
    IS_CONVERTIBLE_TO(bool, !!b);
};

template<typename T>
concept IsSafeNumber = requires
{
    static_cast<Type<typename T::SafeNumberTag_>*>(nullptr);
};

template<typename T>
concept IsRawSafeNumber = IsSafeNumber<T> && IsRaw<T>;

template<typename T>
concept IsSafeBool = IsSafeNumber<T> && requires(T const& v)
{
    requires IsIdentical<decltype(v.value), bool>;
};

template<typename T>
concept IsRawSafeBool = IsSafeBool<T> && IsRaw<T>;

template<typename T>
concept IsResizable = IsSizedRange<T> && requires(T& obj)
{
    obj.resize(8uz);
};

template<typename T, typename... Args_>
concept IsCallable = requires
{
    requires std::is_invocable_v<T, Args_...>;
};
} // namespace stx